package com.ruoyi.business.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.business.service.*;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.dto.PaymentDTO;
import com.ruoyi.common.core.domain.dto.RefundDTO;
import com.ruoyi.common.core.domain.dto.RefundTransactionDTO;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.entity.WsdPaymentRecords;
import com.ruoyi.common.core.domain.entity.WsdRefundTransactions;
import com.ruoyi.common.core.domain.vo.RechargeRefundVO;
import com.ruoyi.common.core.domain.vo.fuYouDTO;
import com.ruoyi.common.utils.Fuyou.FuyouUtil;
import com.ruoyi.common.utils.UserLockUtils;
import com.ruoyi.common.utils.date.DateTimeUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import static com.ruoyi.common.utils.Fuyou.FuyouUtil.generateRandomString;
import static com.ruoyi.common.utils.SecurityUtils.getLoginUser;

/**
 * @Author itmei
 * @Date 2024/5/18 17:05
 * @description:
 * @Title: FuiouNotifyServiceImpl
 * @Package com.ruoyi.business.service
 */
@Slf4j
@Service
public class FuiouNotifyServiceImpl implements FuiouNotifyService {

    // // 商户密钥
    // private static final String MCHNT_KEY = "76f89f70136311ef6895cf3823568604";
    // // 商户号
    // private static final String MCHNT_CD = "0006020F7244153";
    // // 请求URL
    // private static final String URL = "https://aipay-xs.fuioupay.com/aggregatePay/wxPreCreate";
    // //回调地址
    // private static final String NOTIFY_URL = "https://916ee20cs861.vicp.fun/wx/fuiou/notify";

    @Value("${fuiou.mchnt-key}")
    private String mchntKey;

    @Value("${fuiou.mchnt-cd}")
    private String mchntCd;

    @Value("${fuiou.url}")
    private String url;

    @Value("${fuiou.refund-url}")
    private String refundUrl;

    @Value("${fuiou.notify-url}")
    private String notifyUrl;

    @Value("${wx.miniapp.appid}")
    private String appid;


    @Autowired
    private IWsdPaymentRecordsService wsdPaymentRecordsService;
    @Autowired
    private IWsdRefundTransactionsService wsdRefundTransactionsService;
    @Autowired
    private IWsdRechargeRefundService wsdRechargeRefundService;


    /**
     * 回调
     *
     * @param params
     * @return
     */
    @Override
    @Transactional
    public String handleNotify(Map<String, String> params) {
        // 打印通知参数用于调试
        String json = JSON.toJSONString(params);
        log.info("接收付款到通知: " + json);

        // 获取富友返回的签名
        String returnedSign = params.get("sign");

        // 生成本地签名用于比对
        String localSign = generateNotifySign(params);

        // 验证签名
        if (!returnedSign.equals(localSign)) {
            // 签名验证失败
            log.error("签名验证失败");
            return "FAIL";
        }

        // 获取商户订单号
        String mchntOrderNo = params.get("mchnt_order_no");

        // 检查订单是否已经处理过
        if (isOrderProcessed(mchntOrderNo)) {
            // 如果订单已经处理过，直接返回"SUCCESS"
            log.info("订单已处理: " + mchntOrderNo);
            return "SUCCESS";
        }

        // 根据通知内容更新订单状态
        String resultCode = params.get("result_code");
        if ("000000".equals(resultCode)) {
            // 支付成功，更新订单状态
            synchronized (UserLockUtils.getLock(mchntOrderNo)) {
                //根据流水号查询订单
                updateOrderStatus(mchntOrderNo,params);
            }

            log.info("支付成功: " + params.get("result_msg"));
            return "SUCCESS";
        } else {
            // 支付失败，记录错误信息
            // updateOrderStatus(mchntOrderNo, false);
            log.error("支付失败: " + params.get("result_msg"));
            return "FAIL";
        }
    }

    @Autowired
    private IWsdCanteenBalanceService wsdCanteenBalanceService;

    /**
     * 更新订单状态
     * @param mchntOrderNo
     * @param params
     */
    private void updateOrderStatus(String mchntOrderNo, Map<String, String> params) {
        // 这里实现更新订单状态的逻辑
        LambdaQueryWrapper<WsdPaymentRecords> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(WsdPaymentRecords::getTransactionId,mchntOrderNo); //订单号
        WsdPaymentRecords wsdPaymentRecords = wsdPaymentRecordsService.list(wrapper).stream().findFirst().orElse(null);
        String amount = params.get("reserved_settlement_amt"); //交易金额
        String settle = params.get("reserved_fy_settle_dt");//  富友清算日(资金实际到达账户的日期)

        BigDecimal checkAmount = new BigDecimal(amount).divide(new BigDecimal("100"));
        log.info("交易金额："+checkAmount);
        if (wsdPaymentRecords == null){
            log.error("更新订单失败,订单不存在");
            return;
        }
        wsdPaymentRecords.setIsProcessed(wsdPaymentRecords.getIsProcessed() + 1); // 增加处理次数
        wsdPaymentRecords.setTotalFee(checkAmount); // 交易金额
        wsdPaymentRecords.setBalanceFee( checkAmount );
        wsdPaymentRecords.setPayId(params.get("transaction_id"));  //支付流水号
        wsdPaymentRecords.setFeeTime(LocalDateTime.now());  //缴费时间
        wsdPaymentRecords.setReservedFySettleDt(settle); // 富友清算日
        wsdPaymentRecords.setIsPaid(true);
        //根据订单号更新
        LambdaQueryWrapper<WsdPaymentRecords> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(WsdPaymentRecords::getTransactionId,mchntOrderNo);
        wsdPaymentRecordsService.update(wsdPaymentRecords,queryWrapper);
        log.info("更新订单状态成功");
        //更新余额表
        wsdCanteenBalanceService.rechargeBalance(wsdPaymentRecords.getStudentId(),wsdPaymentRecords.getSchoolsId(),checkAmount,wsdPaymentRecords.getMealType());
        log.info("更新余额成功");
    }

    /**
     * 检查订单是否已经处理过
     * @param mchntOrderNo 商户订单号
     * @return 是否已处理
     */
    private boolean isOrderProcessed(String mchntOrderNo) {
        // 订单状态表，其中有字段is_processed表示订单是否已经处理
        LambdaQueryWrapper<WsdPaymentRecords> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(WsdPaymentRecords::getTransactionId,mchntOrderNo);
        WsdPaymentRecords wsdPaymentRecords = wsdPaymentRecordsService.list(wrapper).stream().findFirst().orElse(null);
        return wsdPaymentRecords != null && wsdPaymentRecords.getIsProcessed() > 0;
    }

    /**
     * 富友支付(预下单)
     *
     * @param paymentDTO
     * @param payAmount
     * @param clientIP
     * @param openid
     * @return
     */
    @Override
    @Transactional
    public AjaxResult fuYouPay(PaymentDTO paymentDTO, BigDecimal payAmount, String clientIP, String openid) {
        // 创建参数映射
        Map<String, String> params = new HashMap<>();
        String substring = "15631" + UUID.randomUUID().toString().replace("-", "").substring(0, 20);

        params.put("version", "1.0"); // 版本号
        params.put("mchnt_cd", mchntCd); // 商户号
        params.put("random_str", generateRandomString(32)); // 随机字符串
        params.put("order_amt", payAmount.intValue()+""); // 订单总金额，以分为单位
        params.put("mchnt_order_no",substring); // 订单前缀+随机商户订单号
        params.put("txn_begin_ts", FuyouUtil.getCurrentTimestamp()); // 交易起始时间，格式：yyyyMMddHHmmss
        params.put("goods_des", "食堂充值"); // 商品描述
        params.put("term_id", FuyouUtil.generateRandom8ByteString()); // 终端号
        params.put("term_ip", clientIP); // 终端IP
        params.put("notify_url", notifyUrl); // 通知URL
        params.put("trade_type", "LETPAY"); // 交易类型
        params.put("sub_openid", openid); // 下单sub_openid
        params.put("sub_appid", appid); // sub_appid

        // 打印参数
        log.info("请求富友支付预下单参数=={}",JSON.toJSON(params));

        // 计算签名
        String sign = generatePreOrderSign(params);
        params.put("sign", sign); // 确保签名参数的键名为 "sign"

        // 打印签名
        log.info("请求富友支付签名=={}",sign);

        // 发送请求
        String response = null;
        try {
            response = sendRequest(url,params);
        } catch (Exception e) {
            log.error("发送下单请求失败",e);
            throw new RuntimeException("发送请求失败",e);
        }
        fuYouDTO fuYouDTO = JSON.parseObject(response, fuYouDTO.class);
        if (!"000000".equals(fuYouDTO.getResultCode())){
            throw new RuntimeException("系统繁忙,请稍后重试!");
        }
        log.info("请求富友数据=={}",response);

        //拼装流水表
        SysUser user = getLoginUser().getUser();
        WsdPaymentRecords wsdPaymentRecords = new WsdPaymentRecords();
        wsdPaymentRecords.setTransactionId(substring);   //订单号
        wsdPaymentRecords.setUserId(user.getUserId()); //用户id
        wsdPaymentRecords.setStudentId(paymentDTO.getStudentsId());  //学生id
        wsdPaymentRecords.setStudentName(paymentDTO.getStudentsName());  //学生姓名
        wsdPaymentRecords.setSchoolName(paymentDTO.getSchoolName()); //学校名称
        wsdPaymentRecords.setSchoolsId(user.getSchoolId()); //学校id
        wsdPaymentRecords.setGradeName(paymentDTO.getGradesName()); //年级
        wsdPaymentRecords.setClassName(paymentDTO.getClassesName()); //班级
        wsdPaymentRecords.setIdentityNumber(""); //身份证暂时没有
        wsdPaymentRecords.setParentMobile(user.getPhonenumber());  //家长手机号
        wsdPaymentRecords.setRechargeTime(LocalDateTime.now()); //充值时间
        wsdPaymentRecords.setFeeItemName("学生餐费"); //缴费项目名称
        wsdPaymentRecords.setMealType(paymentDTO.getMealType());  //缴费类型 (0: 早餐, 1: 午餐, 2: 晚餐)
        Integer mealType = paymentDTO.getMealType();
        switch (mealType){
            case 0:
                wsdPaymentRecords.setFeeAccountName("学生早餐费"); //缴费账单名称
                break;
            case 1:
                wsdPaymentRecords.setFeeAccountName("学生午餐费"); //缴费账单名称
                break;
            case 2:
                wsdPaymentRecords.setFeeAccountName("学生晚餐费"); //缴费账单名称
                break;
            default:
                throw new RuntimeException("未知的缴费类型!");
        }
        String currentDateString = DateTimeUtil.getCurrentDateString();
        RechargeRefundVO rechargeRefundVO = wsdRechargeRefundService.existsActiveSemester(user.getSchoolId(),currentDateString);
        if (rechargeRefundVO == null){
            throw new RuntimeException("该学校还没有配置当前学期的时间,没有可用的充值方案!");
        }
        wsdPaymentRecords.setConfigId(rechargeRefundVO.getConfigId()); //学期id
        wsdPaymentRecords.setIsPaid(false); //是否支付 (0: 未支付, 1: 已支付)
        wsdPaymentRecords.setNotifyUrl(notifyUrl); //回调接口
        wsdPaymentRecords.setCode(paymentDTO.getCode()); //下单code
        wsdPaymentRecords.setOpentId(openid); //openid
        wsdPaymentRecords.setSpbillCreateIp(clientIP); //ip
        //进行新增
        wsdPaymentRecordsService.save(wsdPaymentRecords);
        //返回响应
        JSONObject jsonObject = JSON.parseObject(fuYouDTO.getReservedPayInfo());
        log.info("请求富友支付预下单响应=={}",JSON.toJSON(jsonObject));
        // 返回响应(预下单响应)
        return AjaxResult.success(jsonObject);
    }


    // 生成预下单签名
    private String generatePreOrderSign(Map<String, String> params) {
        StringBuilder sb = new StringBuilder();
        // 拼接参数，按文档顺序，使用“|”分隔
        sb.append(params.getOrDefault("mchnt_cd", "")).append("|")
                .append(params.getOrDefault("trade_type", "")).append("|")
                .append(params.getOrDefault("order_amt", "")).append("|")
                .append(params.getOrDefault("mchnt_order_no", "")).append("|")
                .append(params.getOrDefault("txn_begin_ts", "")).append("|")
                .append(params.getOrDefault("goods_des", "")).append("|")
                .append(params.getOrDefault("term_id", "")).append("|")
                .append(params.getOrDefault("term_ip", "")).append("|")
                .append(params.getOrDefault("notify_url", "")).append("|")
                .append(params.getOrDefault("random_str", "")).append("|")
                .append(params.getOrDefault("version", "")).append("|")
                .append(mchntKey); // 最后拼接商户密钥

        // 打印签名字符串用于调试
        System.out.println("预下单签名字符串: " + sb.toString());

        // 使用MD5生成签名
        return DigestUtils.md5Hex(sb.toString().getBytes(StandardCharsets.UTF_8));
    }


    // 生成通知签名
    private String generateNotifySign(Map<String, String> params) {
        StringBuilder sb = new StringBuilder();
        // 拼接参数，按文档顺序，使用“|”分隔
        sb.append(params.getOrDefault("mchnt_cd", "")).append("|")
                .append(params.getOrDefault("mchnt_order_no", "")).append("|")
                .append(params.getOrDefault("settle_order_amt", "")).append("|")
                .append(params.getOrDefault("order_amt", "")).append("|")
                .append(params.getOrDefault("txn_fin_ts", "")).append("|")
                .append(params.getOrDefault("reserved_fy_settle_dt", "")).append("|")
                .append(params.getOrDefault("random_str", "")).append("|")
                .append(mchntKey); // 最后拼接商户密钥

        // 打印签名字符串用于调试
        System.out.println("签名字符串: " + sb.toString());

        // 使用MD5生成签名
        return DigestUtils.md5Hex(sb.toString().getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 生成退款签名
     * @param params 请求参数
     * @return 签名字符串
     */
    private String generateSign(Map<String, String> params) {
        StringBuilder sb = new StringBuilder();
        sb.append(params.get("mchnt_cd")).append("|")
                .append(params.get("order_type")).append("|")
                .append(params.get("mchnt_order_no")).append("|")
                .append(params.get("refund_order_no")).append("|")
                .append(params.get("total_amt")).append("|")
                .append(params.get("refund_amt")).append("|")
                .append(params.get("term_id")).append("|")
                .append(params.get("random_str")).append("|")
                .append(params.get("version")).append("|")
                .append(mchntKey);

        // 打印签名字符串
        System.out.println("签名字符串: " + sb.toString());

        // 使用MD5生成签名
        return DigestUtils.md5Hex(sb.toString().getBytes(StandardCharsets.UTF_8));
    }


    /**
     * 发送HTTP POST请求
     * @param url 请求URL
     * @param params 请求参数
     * @return 响应字符串
     * @throws Exception 异常
     */
    public static String sendRequest(String url, Map<String, String> params) throws Exception {
        // 创建HttpClient
        CloseableHttpClient httpClient = HttpClients.createDefault();
        // 创建HttpPost请求
        HttpPost httpPost = new HttpPost(url);
        httpPost.setHeader("Content-Type", "application/json");

        // 将参数转换为JSON字符串
        ObjectMapper objectMapper = new ObjectMapper();
        StringEntity entity = new StringEntity(objectMapper.writeValueAsString(params), StandardCharsets.UTF_8);
        httpPost.setEntity(entity);

        // 打印请求内容
        System.out.println("请求内容: " + objectMapper.writeValueAsString(params));

        // 执行请求并获取响应
        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            String responseString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
            // 打印响应内容
            System.out.println("响应内容: " + responseString);
            return responseString;
        }
    }


    /**
     * 退款
     *
     * @param refundDTO
     * @return
     */
    @Override
    @Transactional
    public String refund(RefundDTO refundDTO)  {
        BigDecimal payAmount = refundDTO.getTotalAmt().multiply(new BigDecimal(100));
        BigDecimal multiply = refundDTO.getRefundAmt().multiply(new BigDecimal(100));
        String substring = "15631" + UUID.randomUUID().toString().replace("-", "").substring(0, 20);
        // 创建参数映射
        Map<String, String> params = new HashMap<>();
        params.put("version", "1.0"); // 版本号
        params.put("mchnt_cd", mchntCd); // 富友分配的商户号
        params.put("term_id", generateRandomString(8)); // 终端号，备用字段，传任意 8 字节字符即可
        params.put("random_str", generateRandomString(32)); // 随机字符串
        params.put("mchnt_order_no", refundDTO.getMchntOrderNo()); // 商户系统内部的订单号
        params.put("refund_order_no", substring); // 商户退款单号(也要加订单前缀)
        //params.put("order_type", refundDTO.getOrderType()); // 订单类型
        params.put("order_type", "LETPAY"); // 订单类型
        params.put("total_amt", payAmount.intValue()+""); // 订单金额 (分为单位的整数)
        params.put("refund_amt", multiply.intValue()+""); // 退款金额 (退款金额以分为单位)
        if (refundDTO.getOperatorId() != null) {
            params.put("operator_id", refundDTO.getOperatorId()); // 操作员
        }
        if (refundDTO.getReservedFyTermId() != null) {
            params.put("reserved_fy_term_id", refundDTO.getReservedFyTermId()); // 富友终端号
        }
        if (refundDTO.getReservedOrigiDt() != null) {
            params.put("reserved_origi_dt", refundDTO.getReservedOrigiDt()); // 原交易日期 不填该值支持30天内的交易进行退款,填写该值，支持180天。数据库有字段后续可以进行查询
        }
        if (refundDTO.getReservedAddnInf() != null) {
            params.put("reserved_addn_inf", refundDTO.getReservedAddnInf()); // 附加数据
        }

        // 生成签名
        String sign = generateSign(params);
        params.put("sign", sign); // 确保签名参数的键名为 "sign"

        // 发送请求
        String response = null;
        try {
            response = sendRequest(refundUrl,params);
        } catch (Exception e) {
            log.error("发送退款请求失败",e);
            throw new RuntimeException("发送退款请求失败",e);
        }
        log.info("发送退款请求响应参数");
        RefundTransactionDTO refundTransaction = JSON.parseObject(response, RefundTransactionDTO.class);

        if ("000000".equals(refundTransaction.getResultCode())) {
            // 退款成功
            WsdRefundTransactions wsdRefundTransactions = new WsdRefundTransactions();
            wsdRefundTransactions.setRefundDate(LocalDateTime.now()); //退款时间
            wsdRefundTransactions.setStatus(2); //状态
            wsdRefundTransactions.setRefundId(refundTransaction.getRefundId()); //退款流水号
            wsdRefundTransactions.setRefundOrderNo(substring); //退款订单
            wsdRefundTransactions.setTransactionId( refundTransaction.getTransactionId() );
            wsdRefundTransactions.setUserId( refundDTO.getUserId() );
            wsdRefundTransactions.setStudentId( refundDTO.getStudentId() );
            wsdRefundTransactions.setSchoolId(refundDTO.getSchoolsId()  );
            String reservedRefundAmt = refundTransaction.getReservedRefundAmt();
            wsdRefundTransactions.setRefundAmount(new BigDecimal(reservedRefundAmt).divide(new BigDecimal(100))); //退款金额
            LambdaQueryWrapper<WsdRefundTransactions> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(WsdRefundTransactions::getTransactionId, refundDTO.getMchntOrderNo());  //根据订单号进行查询
            //boolean update = wsdRefundTransactionsService.update(wsdRefundTransactions, wrapper);
            boolean save = wsdRefundTransactionsService.save(wsdRefundTransactions);
            if (!save) {
                log.info("退款失败");
                return "退款失败";
            }
        }else {
//            WsdRefundTransactions wsdRefundTransactions = new WsdRefundTransactions();
//            wsdRefundTransactions.setRefundDate(LocalDateTime.now()); //退款时间
//            wsdRefundTransactions.setStatus(1); //状态
//            wsdRefundTransactions.setRefundId(refundTransaction.getRefundId()); //退款流水号
//            wsdRefundTransactions.setRefundOrderNo(substring); //退款订单
//            wsdRefundTransactions.setTransactionId( refundTransaction.getTransactionId() );
//            wsdRefundTransactions.setUserId( refundDTO.getUserId() );
//            wsdRefundTransactions.setStudentId( refundDTO.getStudentId() );
//            wsdRefundTransactions.setSchoolId(refundDTO.getSchoolsId()  );
//            String reservedRefundAmt = refundTransaction.getReservedRefundAmt();
//            wsdRefundTransactions.setRefundAmount(new BigDecimal(reservedRefundAmt).divide(new BigDecimal(100))); //退款金额
//            wsdRefundTransactionsService.save(wsdRefundTransactions);
            String returnMsg = refundTransaction.getResultMsg();
            return returnMsg;
        }
        return "退款成功";
    }
}
